package spacegame;

/**
 * @author Adam ada2358@ccs.neu.edu
 * @param <T>
 */
public abstract class Option<T> {
  public static <T> Option<T> none(){
    return new None<T>();
  }
  
  public static <T> Option<T> some(T a){
    return new Some<T>(a);
  }
  
  // Is this Option none or some?
  public abstract boolean isNone();
  
  // Getter for this Option's value.
  public abstract T valOf();
  
  // Stringify this Option.
  public abstract String toString();
  
  // Is this Option eqaual to the given object?
  public abstract boolean equals(Object obj);
  
  // The hashcode for this Option.
  public abstract int hashCode();
  
  private static class None<T> extends Option<T> {
    public None() {};
    
    @SuppressWarnings("unchecked") public boolean equals(Object obj) {
      return (obj instanceof Option<?>) && ((None<T>) obj).isNone();
    }
    
    public int hashCode() {
      return 0;
    }
    
    public boolean isNone() {
      return true;
    }
    
    public String toString() {
      return "none";
    }
    
    public T valOf() {
      throw new RuntimeException("Can't get the value of an empty Option");
    }
  }
  
  private static class Some<T> extends Option<T> {
    private T value;
    
    public Some(T val) {
      value = val;
    }
    
    @SuppressWarnings("unchecked") public boolean equals(Object obj) {
      return (obj instanceof Option<?>) && !((Some<T>) obj).isNone()
             && ((Some<T>) obj).valOf().equals(this.value);
    }
    
    public int hashCode() {
      return this.value.hashCode();
    }
    
    public boolean isNone() {
      return false;
    }
    
    public String toString() {
      return "some(" + value.toString() + ")";
    }
    
    public T valOf() {
      return this.value;
    }
  }
}
